/*********************************************
 * Copyright (c) 2010 ICBC.
 * All rights reserved.
 * Created on 2011-7-4 

 * Contributors:
 *     kfzx-lixy01 - initial implementation
 *********************************************/

package com.icbc.ctpdemo.user.opstep;

import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;

import com.icbc.cte.base.CTEConstance;
import com.icbc.cte.base.TranFailException;
import com.icbc.cte.logging.Log;
import com.icbc.cte.logging.LogFactory;
import com.icbc.ctp.core.IContext;
import com.icbc.ctp.core.annotations.Input;
import com.icbc.ctp.core.annotations.Output;
import com.icbc.ctp.core.impl.OperationStep;
import com.icbc.ctp.util.charset.Unicode;

/**
 * 该交易步骤用于对密码字符串进行校验.
 * 
 * <pre>
 * 该交易步骤用于对密码字符串进行校验,密码必须符合一定规则。 &lt;br&gt;
 * &lt;ul&gt;
 * 参数说明
 * &lt;li&gt;password        - 密码
 * &lt;/ul&gt;
 * &lt;ul&gt;
 * 返回结果
 * &lt;li&gt;0 - 正常
 * &lt;li&gt;-1 - 异常
 * &lt;/ul&gt;
 * </pre>
 */
@Input(name = "password", type = "java:java.lang.String", mustInput = true, isVariable = true, isArray = false, description = "密码")
@Output(name = "isValid", type = "java:java.lang.String", isArray = false, description = "是否通过校验")
public class PasswordCheckerOpStep extends OperationStep {

	private Log ctpLog = LogFactory.getCtpLog(PasswordCheckerOpStep.class);
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.icbc.ctp.core.impl.OperationStep#execute(com.icbc.ctp.core.IContext)
	 */
	@Override
	protected int execute(IContext context) throws TranFailException {
		String pwdMinCharLengthStr = CTEConstance.getEnvProperty("pwdMinCharLength");
		String pwdMaxCharLengthStr = CTEConstance.getEnvProperty("pwdMaxCharLength");
		String seqLengthStr = CTEConstance.getEnvProperty("seqLength");
		String pwdCannotIncludeStr = CTEConstance.getEnvProperty("pwdCannotInclude");

		int pwdMinCharLength = 8;
		int pwdMaxCharLength = 20;
		int seqLength = 3;
		String[] pwdCannotInclude = null;
		boolean resultFlag = true;
		String errMsg = "";
		String errCode = "";
		String errParam = "";
		
		try {
			if (null != pwdMinCharLengthStr && !"".equals(pwdMinCharLengthStr)) {
				pwdMinCharLength = Integer.parseInt(pwdMinCharLengthStr.trim());
//				if(pwdMinCharLength < 7){
//					pwdMinCharLength = 7;//如果密码最小长度小于7，则赋值为7
//				}
			}
		} catch (NumberFormatException e) {
			pwdMinCharLength = 8;// 如果有类型转换异常，pwdMinCharLength设为默认值8
		}
		
		try {
			if (null != pwdMaxCharLengthStr && !"".equals(pwdMaxCharLengthStr)) {
				pwdMaxCharLength = Integer.parseInt(pwdMaxCharLengthStr.trim());
//				if(pwdMaxCharLength > 20){
//					pwdMaxCharLength = 20;//如果密码最大长度大于20，则赋值为20
//				}
			}
		} catch (NumberFormatException e) {
			pwdMaxCharLength = 20;// 如果有类型转换异常，pwdMaxCharLength设为默认值20
		}

		try {
			if (null != seqLengthStr && !"".equals(seqLengthStr)) {
				seqLength = Integer.parseInt(seqLengthStr);
//				if(seqLength < 3){
//					seqLength = 3;//如果连续相同/顺序/逆序的位数小于3，则seqLength赋值为3
//				}
			}
		} catch (NumberFormatException e) {
			seqLength = 3;
		}
		
		if(null != pwdCannotIncludeStr && !"".equals(pwdCannotIncludeStr)){
			pwdCannotInclude = pwdCannotIncludeStr.split(",");
		}
		
		String password = (String) getInputValue(context, "password");

		// 测试数据
		//password = "13579246810";
		password = password.toUpperCase();// 全部转成大写
		// password = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		
		String digitRegexStr = "[0-9]";//数字正则
		String letterRegexStr = "[A-Z]";//字母正则
		
		try{
			if ("".equals(password)) {
				errMsg = "password is null";// 空密码
				errCode = "PasswordCheckerOpStep001";
				resultFlag = false;
			} else if (pwdMinCharLength > password.length()) {
				errMsg = "password is too short";// 密码长度小于配置的最小长度
				errCode = "PasswordCheckerOpStep002";
				errParam = pwdMinCharLengthStr;
				resultFlag = false;
			} else if(pwdMaxCharLength < password.length()){
				errMsg = "password is too long";//密码长度大于配置的最大长度
				errCode = "PasswordCheckerOpStep003";
				errParam = pwdMaxCharLengthStr;
				resultFlag = false;
			}else if(!Pattern.compile(digitRegexStr).matcher(password).find() || !Pattern.compile(letterRegexStr).matcher(password).find()){
				errMsg = "password must including letters and digits"; //密码必须包含数字和字符
				errCode = "PasswordCheckerOpStep004";
				resultFlag = false;
			}else {
				
				if (null != pwdCannotInclude && pwdCannotInclude.length > 0) {
					for (int k = 0; k < pwdCannotInclude.length; k++) {
						if (null != pwdCannotInclude[k]
								&& password.contains(pwdCannotInclude[k].trim().toUpperCase())) {
							// 包含有不能包含的字符串
							errMsg = "password including invalid word: " + pwdCannotInclude[k].trim();
							errCode = "PasswordCheckerOpStep005";
							errParam = pwdCannotInclude[k].trim();
							resultFlag = false;
							break;
						}
					}
				}

				if (resultFlag) {
					String unicodePassword = Unicode.string2Unicode(password);// 将密码转成unicode字符串
					String[] unicodePwdChars = unicodePassword.split(" ");// 将unicode的密码分割成字符的unicode字符串

					int end = seqLength - 1;
					List<Integer> tmpHex = new Vector<Integer>();// 用来判断密码内连续位数字符是否字典顺序用

					for (int i = 0; i < unicodePwdChars.length - end; i++) {
						tmpHex.clear();// 每次清空，将密码内连续seqLength位字符的unicode存入tmpHex中
						for (int j = 0; j < seqLength; j++) {
							tmpHex.add(Integer.parseInt(
									unicodePwdChars[i + j].substring(1), 16));// 将字符的unicode码去掉U，且换成16进制int类型，用作比较
						}

						if (0 != checkInvalidPwdType(tmpHex, 1)) {
							// 连续相同不通过
							errMsg = "password has " + seqLength + " consequent identical characters";
							errCode = "PasswordCheckerOpStep006";
							errParam = Integer.toString(seqLength);
							resultFlag = false;
							break;
						} else if (0 != checkInvalidPwdType(tmpHex, 2)) {
							// 连续升序不通过
							errMsg = "password has " + seqLength + " consequent ascendant characters";
							errCode = "PasswordCheckerOpStep007";
							errParam = Integer.toString(seqLength);
							resultFlag = false;
							break;
						} else if (0 != checkInvalidPwdType(tmpHex, 3)) {
							// 连续降序不通过
							errMsg = "password has " + seqLength + " consequent descend characters";
							errCode = "PasswordCheckerOpStep008";
							errParam = Integer.toString(seqLength);
							resultFlag = false;
							break;
						}
					}
				}
			}
			
			if(!resultFlag){
				throw new Exception();
			}
		}catch (Exception e) {
			ctpLog.error("password is invalid for the reason: " + errMsg);
			TranFailException ctee = new TranFailException(
					errCode, "", e, new Object[] { errParam });
			context.setValueAt("opErrMsg", ctee.getLocalizedMessage());
			context.setValueAt("out_flag", "-5");
			inException(context, ctee);
			return -1;
		}

		setOutputValue(context, "isValid", resultFlag);
		return 0;
	}

	/*
	 * checkType：1-连续相同；2-连续升序；3-连续降序 返回值：0-密码通过校验；1-连续相同不通过；2-连续升序不通过；3-连续降序不通过
	 */
	private int checkInvalidPwdType(List<Integer> tmpHex, int checkType) {
		int result = 0;
		int identicalCount = 0;
		int ascCount = 0;
		int descCount = 0;
		for (int i = 1; i < tmpHex.size(); i++) {
			if (1 == checkType && isIdentical(tmpHex.get(i), tmpHex.get(i - 1))) {
				// 连续相同的判断
				identicalCount++;
			}
			if (isDigitOrLetter(tmpHex.get(i))
					&& isDigitOrLetter(tmpHex.get(i - 1))) {
				// 只有是数字和字母的字符才需要做连续升序/降序的判断
				if (2 == checkType && isAsc(tmpHex.get(i), tmpHex.get(i - 1))) {
					// 连续升序
					ascCount++;
				} else if (3 == checkType
						&& isDesc(tmpHex.get(i), tmpHex.get(i - 1))) {
					// 连续降序
					descCount++;
				}
			}
		}

		if (identicalCount == tmpHex.size() - 1) {
			result = 1;
		}
		if (ascCount == tmpHex.size() - 1) {
			result = 2;
		}
		if (descCount == tmpHex.size() - 1) {
			result = 3;
		}
		return result;
	}

	/**
	 * 只有字母字符和数字字符才需要做连续判断
	 */
	private boolean isDigitOrLetter(int hex) {
		int digitBegin = Integer.parseInt("0030", 16);
		int digitEnd = Integer.parseInt("0039", 16);
		int letterBegin = Integer.parseInt("0041", 16);
		int letterEnd = Integer.parseInt("005A", 16);

		if (hex >= digitBegin && hex <= digitEnd) {
			return true;
		} else if (hex >= letterBegin && hex <= letterEnd) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 判断连续字符是否一样
	 */
	private boolean isIdentical(int thisPos, int prevPos) {
		return thisPos == prevPos;
	}

	/**
	 * 判断连续字符是否连续降序
	 */
	private boolean isDesc(int thisPos, int prevPos) {
		return thisPos == prevPos - 1;
	}

	/**
	 * 判断连续字符是否连续升序
	 */
	private boolean isAsc(int thisPos, int prevPos) {
		return thisPos == prevPos + 1;
	}

}
